#include "QAppMainWindow.h"
#include "QtGui/QPainter.h"
#include "QtGui/QSplitter.h"
#include "QtGui/QLayout.h"
#include "QtGui/QSpinBox.h"
#include "QtGui/QPushButton.h"

#include <iostream>
#include <algorithm>
#include <limits>
#include <math.h>
#include <assert.h>

QAppMainWindow::QAppMainWindow( QWidget* _parent )
	:	QFrame(_parent), mWaveForm(NULL), mWaveFormWindow(NULL)
{
	bool ret;

	//mWaveForm = WaveForm::readFromWave( "d:\\enmoto.wav" );

//	mWaveForm = WaveForm::readFromRaw( "D:\\06 - Paris_Bordeaux_Marseille_8000_16_1.raw" );
	//mWaveForm = WaveForm::readFromRaw( "D:\\result.raw" );
	mWaveForm = WaveForm::readFromRaw( "D:\\enmoto.wav" );
	//mWaveForm = WaveForm::readFromRaw( "D:\\apied.wav" );
//	mWaveForm = WaveForm::readFromRaw( "D:\\05 - La440_8000_16_1.raw");
	//WaveForm = WaveForm::createFromSinusoid( 1024, 1024/32*2.f, 1.f );

	
	assert( mWaveForm );
	mWaveFormWindow = new SampleWindow( mWaveForm );
	mWaveFormWindow->registerListener( this );
	
	QSplitter* splitter = new QSplitter(this);
	splitter->resize( width(), height() );
	splitter->setHandleWidth(5);
	
	QSplitter* verticalSplitter = new QSplitter(Qt::Vertical, splitter);
	verticalSplitter->setHandleWidth(5);

	mWaveFormView = new QWaveFormView(verticalSplitter);
	mWaveFormView->SetWaveFormWindow( mWaveFormWindow );
	
	mSpectrumView = new QSpectrumView(verticalSplitter);
	mSpectrumView->setWaveFormWindow( mWaveFormWindow );

		
	QHBoxLayout* mainlayout = new QHBoxLayout;
	mainlayout->addWidget(splitter);
	setLayout( mainlayout);
	
	QFrame* rightFrame= new QFrame(splitter);
	QList<int> sizes = splitter->sizes();
	sizes[0] = 50;
	splitter->setSizes( sizes  );
	
	QPushButton *button2 = new QPushButton("Previous");
	QPushButton *button3 = new QPushButton("Play");
	ret = connect( button3, SIGNAL( pressed() ), this, SLOT( buttonPressed() ) );

	mSampleWindowWidthSpinBox = new QSpinBox();
	mSampleWindowWidthSpinBox->setMaximum( 4096 );
	mSampleWindowWidthSpinBox->setMinimum( 0 );
	ret = connect( mSampleWindowWidthSpinBox, SIGNAL( valueChanged(int) ), this, SLOT( SetSampleWindowWidth(int) ) );
	assert(ret);
	
	mSampleWindowPositionSpinBox = new QSpinBox();
	mSampleWindowPositionSpinBox->setMaximum( 100000 );
	mSampleWindowPositionSpinBox->setMinimum( -100 );
	ret = connect( mSampleWindowPositionSpinBox, SIGNAL( valueChanged(int) ), this, SLOT( SetSampleWindowPosition(int) ) );
	assert(ret);

	QVBoxLayout *layout = new QVBoxLayout;
	layout->addWidget(button2);
	layout->addWidget(button3);
	layout->addWidget(mSampleWindowWidthSpinBox);
	layout->addWidget(mSampleWindowPositionSpinBox);
	layout->addStretch();

	rightFrame->setLayout(layout);
	rightFrame->show();	

	

	mOtherWaveForm = new WaveForm();
	mOtherWaveFormWindow = new SampleWindow( mOtherWaveForm );


	QSpectrumView* sp = new QSpectrumView(verticalSplitter);
//	sp->resize( 640,300 );
	sp->show();
	sp->setWaveFormWindow( mOtherWaveFormWindow );

	mOtherWaveFormView = new QWaveFormView(verticalSplitter);
//	mOtherWaveFormView->resize( 640,300 );
	mOtherWaveFormView->show();
	mOtherWaveFormView->SetWaveFormWindow( mOtherWaveFormWindow );


	sampleWindowChanged( mWaveFormWindow );

	mWaveFormWindow->setPosition(0);
	mWaveFormWindow->setWidth(512);

}

void QAppMainWindow::SetSampleWindowWidth( int width )
{
	mWaveFormWindow->setWidth( width );
}

void QAppMainWindow::SetSampleWindowPosition( int position )
{
	mWaveFormWindow->setPosition( position );
}

void QAppMainWindow::filterFFTBins( std::size_t minBin, std::size_t maxBin, WaveForm::FFTBins& fftBins )
{
	std::size_t min = std::min( minBin, fftBins.size() );
	std::size_t max = std::min( maxBin, fftBins.size() );

	for ( std::size_t i=0; i<min; ++i )
	{
		fftBins[i].first = 0.f;
		fftBins[i].second = 0.f;
	}

	for ( std::size_t i=max; i<fftBins.size(); ++i )
	{
		fftBins[i].first = 0.f;
		fftBins[i].second = 0.f;
	}
}

void QAppMainWindow::sampleWindowChanged( SampleWindow* sampleWindow )
{
	mSampleWindowWidthSpinBox->setValue( sampleWindow->getWidth() );
	mSampleWindowPositionSpinBox->setValue( sampleWindow->getPosition() );

	
	WaveForm::NormalizedSamples normalizedSamples;
	mWaveFormWindow->getNormalizedSamples( normalizedSamples );
	
	WaveForm::FFTBins fftBins;
	WaveForm::calculateFFT( normalizedSamples, fftBins );
	
	filterFFTBins( 20, fftBins.size(), fftBins );
		
	
	WaveForm::calculateInverseFFT( fftBins, normalizedSamples );
	
	WaveForm::Samples samples;
	WaveForm::unnormalizeSamples( normalizedSamples, samples );

/*	WaveForm::doStuff( normalizedSamples );
	WaveForm::Samples samples;
	WaveForm::unnormalizeSamples( normalizedSamples, samples );
*/	mOtherWaveForm->getSamples() = samples;
	mOtherWaveFormWindow->fireChangeEvent();
	mOtherWaveFormWindow->setWidth( mWaveFormWindow->getWidth() );
}

void QAppMainWindow::buttonPressed()
{
	WaveForm result;
	WaveForm::Samples& resultSamples = result.getSamples();

	std::size_t waveFormSize = mWaveForm->getSamples().size();

	std::size_t windowSize = 512;
	std::size_t numWindows = waveFormSize / windowSize;

	SampleWindow window( mWaveForm );
	window.setWidth( windowSize );
	

	WaveForm::NormalizedSamples normalizedSamples;
	WaveForm::FFTBins fftBins;
	WaveForm::Samples samples;

/*	for ( std::size_t i=0; i<numWindows; ++i )
	{
		window.setPosition( i*windowSize );

		window.getNormalizedSamples( normalizedSamples );
		WaveForm::calculateFFT( normalizedSamples, fftBins );
	
		// Tweak things a bit!
		if ( fftBins.size()>16 )
		{
	//		for ( std::size_t i=0; i<16; ++i )
	//		{
	//			fftBins[i].first *= 0.01f;
	//			fftBins[i].second *= 0.01f;
	//		}
		}
	
		WaveForm::calculateInverseFFT( fftBins, normalizedSamples );
		WaveForm::unnormalizeSamples( normalizedSamples, samples );

		resultSamples.insert( resultSamples.end(), samples.begin(), samples.end() );
	}
*/
	WaveForm powerGraph;
	WaveForm::NormalizedSamples powerGraphSamples;
	
	WaveForm averageGraph;
	WaveForm::NormalizedSamples averageGraphSamples;
	
	WaveForm cutSound;
	WaveForm::Samples& cutSoundSamples = cutSound.getSamples();
	

	for ( std::size_t i=0; i<numWindows*2; ++i )
	{
		window.setPosition( i*(windowSize/2) );

		window.getNormalizedSamples( normalizedSamples );
		WaveForm::calculateFFT( normalizedSamples, fftBins );
	
		// Tweak things a bit!
		filterFFTBins( 30, fftBins.size(), fftBins );
	
		WaveForm::calculateInverseFFT( fftBins, normalizedSamples );
		WaveForm::unnormalizeSamples( normalizedSamples, samples );

		WaveForm::Samples::const_iterator start = samples.begin() + (windowSize/4);
		WaveForm::Samples::const_iterator end = start + (windowSize/2);

		resultSamples.insert( resultSamples.end(), start, end );

	//float powerLevel = WaveForm::calculatePowerLevel( fftBins ) / 5.f ;
		//for ( std::size_t j=0; j<windowSize/2; ++j )
		//	powerGraphSamples.push_back( powerLevel  );
	}
	


	WaveForm::normalizeSamples( resultSamples, powerGraphSamples );
	windowSize /= 8;
	numWindows *= 8;
	for ( std::size_t i=0; i<numWindows; ++i )
	{
		float max = 0.f;
		for ( std::size_t j=0; j<windowSize; ++j )
		{
			powerGraphSamples[i * windowSize + j] = abs( powerGraphSamples[i * windowSize + j] );
			
			max = std::max( powerGraphSamples[i * windowSize + j], max );
		}
		
		//average/=windowSize;
		for ( std::size_t j=0; j<windowSize; ++j )
		{
		//	averageGraphSamples.push_back(average);
			averageGraphSamples.push_back(max);
		}
	}

	
	float threshold = 0.18f;
	int numSamplesAfterPeak = 0;
	for ( std::size_t i=0; i<averageGraphSamples.size(); ++i )
	{
		if (averageGraphSamples[i] >= threshold)
		{
			cutSoundSamples.push_back( mWaveForm->getSamples()[i] );
			numSamplesAfterPeak = 8000 * 1.5f;
		}
		else if ( numSamplesAfterPeak >0 )
		{
			float factor = 1.f;
			if ( numSamplesAfterPeak<500 )
			{
				factor = numSamplesAfterPeak / 500.f;
			}
			cutSoundSamples.push_back( mWaveForm->getSamples()[i] * factor );
			numSamplesAfterPeak--;
		}
		else
			cutSoundSamples.push_back(0);
	}
	

	WaveForm::unnormalizeSamples( powerGraphSamples, powerGraph.getSamples() );
	powerGraph.writeToWave( "d:\\resultPower.wav" );

	WaveForm::unnormalizeSamples( averageGraphSamples, averageGraph.getSamples() );
	averageGraph.writeToWave( "d:\\resultPowerAverage.wav" );

	cutSound.writeToWave( "d:\\resultCut.wav" );


	result.writeToWave( "d:\\result.wav" );
}